# coding=utf8

from cefpython3 import cefpython
import os,sys
import pygtk
pygtk.require('2.0')
import gtk
import gobject
from Singleton import *
#
# Globals
#
g_applicationSettings = {
    "debug": False,
    "log_severity": cefpython.LOGSEVERITY_INFO,

    # These directories must be set on Linux
    "locales_dir_path": cefpython.GetModuleDirectory()+"/locales",
    "resources_dir_path": cefpython.GetModuleDirectory(),
    "browser_subprocess_path": "%s/%s" % (
        cefpython.GetModuleDirectory(), "subprocess"),

     "unique_request_context_per_browser": True,
    "downloads_enabled": False,
    "remote_debugging_port": -1,
    "ignore_certificate_errors": True,
}
g_browserSettings = {}

def GetApplicationPath(file= None):
    import re, os, platform
    if not hasattr(GetApplicationPath, "dir"):
        if hasattr(sys, "frozen"):
            dir = os.path.dirname(sys.executable)
        elif "__file__" in globals():
            dir = os.path.dirname(os.path.realpath(__file__))
        else:
            dir = os.getcwd()
        GetApplicationPath.dir = dir
    # If file is None return current directory without trailing slash.
    if file is None:
        file = ""
    # Only when relative path.
    if not file.startswith("/") and not file.startswith("\\") and (
            not re.search(r"^[\w-]+:", file)):
        path = GetApplicationPath.dir + os.sep + file
        if platform.system() == "Windows":
            path = re.sub(r"[/\\]+", re.escape(os.sep), path)
        path = re.sub(r"[/\\]+$", "", path)
        return path
    return str(file)

def ExceptHook(excType, excValue, traceObject):
    import traceback, os, time, codecs
    errorMsg = "\n".join(traceback.format_exception(excType, excValue,
            traceObject))
    errorFile = GetApplicationPath("error.log")
    try:
        appEncoding = cefpython.g_applicationSettings["string_encoding"]
    except:
        appEncoding = "utf-8"
    if type(errorMsg) == bytes:
        errorMsg = errorMsg.decode(encoding=appEncoding, errors="replace")
    try:
        with codecs.open(errorFile, mode="a", encoding=appEncoding) as fp:
            fp.write("\n[%s] %s\n" % (
                    time.strftime("%Y-%m-%d %H:%M:%S"), errorMsg))
    except:
        print ("[pygtk_.py]: WARNING: failed writing to error file: %s" % (
                errorFile))
    # Convert error message to ascii before print ing, otherwise
    # you may get error like this:
    # | UnicodeEncodeError: 'charmap' codec can't encode characters
    errorMsg = errorMsg.encode("ascii", errors="replace")
    errorMsg = errorMsg.decode("ascii", errors="replace")
    print ("\n"+errorMsg+"\n")
    cefpython.QuitMessageLoop()
    cefpython.Shutdown()
    os._exit(1)

class CookieVisitor:

    def __init__(self, cookie_path): #"./qixin.cookie"
        import os.path
        if os.path.exists(cookie_path):
            os.remove(cookie_path)

        self.__cookie_path__ = cookie_path

        self.__cookie_cb__ = None

    def Visit(self, cookie, count, total, deleteCookie):
        if count == 0:
            print ("\n[WebkitLoginHelper] CookieVisitor.Visit(): total cookies: %s"\
                    % total)
        print ("\n[WebkitLoginHelper] CookieVisitor.Visit(): cookie:")
        cookieDict = cookie.Get()
        print ("    "+str(cookieDict))
        print ("\n[WebkitLoginHelper] Write cookie to file")
        self.__save_file = open(self.__cookie_path__, "a+")
        self.__save_file.write("%s=%s"%(cookieDict['name'], cookieDict['value']))
        self.__save_file.write("\r\n")
        self.__save_file.close()
        # True to continue visiting cookiesa
        if self.__cookie_cb__:
            self.__cookie_cb__()
        return True

    def SetOnCookieStoreCallback(self, cb):
        self.__cookie_cb__ = cb

class ClientHandler:
    mainBrowser = None # May be None for global client callbacks.

    def __init__(self, redirect_url, cookie_path):
        self.__redirect_url__ = redirect_url
        self.__cookie_path__ = cookie_path

    def __VisitUrlCookies(self):
        # Need to keep the reference alive.
        self.cookieVisitor = CookieVisitor(self.__cookie_path__)
        self.cookieVisitor.SetOnCookieStoreCallback(
            self.__OnCookieStored
        )
        cookieManager = self.mainBrowser.GetUserData("cookieManager")
        if not cookieManager:
            print ("\n[WebkitLoginHelper] Cookie manager not yet created! Visit"\
                    " the cookietester website first and create some cookies")
            return
        cookieManager.VisitAllCookies(
            self.cookieVisitor)

    def __OnCookieStored(self):
        if self.mainBrowser:
            self.mainBrowser.CloseBrowser(True)

    # -------------------------------------------------------------------------
    # RequestHandler
    # -------------------------------------------------------------------------
    def GetCookieManager(self, browser, mainUrl):
        if not browser:
            return None
        cookieManager = browser.GetUserData("cookieManager")
        if cookieManager:
            return cookieManager
        else:
            print ("[WebkitLoginHelper] RequestHandler::GetCookieManager():"\
                    " created cookie manager")
            cookieManager = cefpython.CookieManager.CreateManager("")
            browser.SetUserData("cookieManager", cookieManager)
            return cookieManager

    def _OnCertificateError(self, certError, requestUrl, callback):
        callback.Continue(True)
        return True

    def _OnBeforePluginLoad(browser, mimeType, pluginUrl, topOriginUrl, info):
        return False

    # -------------------------------------------------------------------------
    # LifespanHandler
    # -------------------------------------------------------------------------
    def _OnAfterCreated(self, browser):
        # This is a global callback set using SetGlobalClientCallback().
        print ("[WebkitLoginHelper] LifespanHandler::_OnAfterCreated()")
        print ("    browserId=%s" % browser.GetIdentifier())

    def DoClose(self, browser):
        print ("[WebkitLoginHelper] LifespanHandler::DoClose()")
        print ("    browserId=%s" % browser.GetIdentifier())

    def OnBeforeClose(self, browser):
        print ("[WebkitLoginHelper] LifespanHandler::OnBeforeClose")
        print ("    browserId=%s" % browser.GetIdentifier())

    # -------------------------------------------------------------------------
    # LoadHandler
    # -------------------------------------------------------------------------
    def OnLoadEnd(self, browser, frame, httpStatusCode):
        print ("[WebkitLoginHelper] LoadHandler::OnLoadEnd()")
        print ("    frame url = %s" % frame.GetUrl()[:100])
        # For file:// urls the status code = 0
        print ("    http status code = %s" % httpStatusCode)
        # Tests for the Browser object methods
        render_url = frame.GetUrl()[:100]
        if httpStatusCode == 200 and render_url == self.__redirect_url__:
            self.__VisitUrlCookies()

class GtkBrowserForm:
    mainWindow = None
    container = None
    browser = None
    exiting = None
    clientHandler = None

    def __init__(self, loginUrl, redirectUrl, cookiePath):
        gobject.timeout_add(10, self.OnTimer)

        self.mainWindow = gtk.Window(gtk.WINDOW_TOPLEVEL)
        self.mainWindow.connect('destroy', self.OnExit)
        self.mainWindow.set_size_request(width=1300, height=600)
        self.mainWindow.set_title('S-spider Login tool')
        self.mainWindow.realize()

        self.container = gtk.DrawingArea()
        self.container.set_property('can-focus', True)
        self.container.connect('size-allocate', self.OnSize)
        self.container.show()

        table = gtk.Table(3, 1, homogeneous=False)
        self.mainWindow.add(table)
        table.attach(self.container, 0, 1, 2, 3)
        table.show()

        # Global client callbacks must be set before browser is created.
        self.clientHandler = ClientHandler(redirectUrl, cookiePath)
        cefpython.SetGlobalClientCallback("OnCertificateError",
                self.clientHandler._OnCertificateError)
        cefpython.SetGlobalClientCallback("OnBeforePluginLoad",
                self.clientHandler._OnBeforePluginLoad)
        cefpython.SetGlobalClientCallback("OnAfterCreated",
                self.clientHandler._OnAfterCreated)

        windowID = self.container.get_window().handle
        windowInfo = cefpython.WindowInfo()
        windowInfo.SetAsChild(windowID)

        self.browser = cefpython.CreateBrowserSync(windowInfo,
                browserSettings=g_browserSettings,
                navigateUrl=loginUrl)

        self.clientHandler.mainBrowser = self.browser
        self.browser.SetClientHandler(self.clientHandler)

        self.mainWindow.show()
        self.mainWindow.get_window().focus()

    def OnWidgetClick(self, widget, data):
        self.mainWindow.get_window().focus()

    def OnTimer(self):
        if self.exiting:
            return False
        cefpython.MessageLoopWork()
        return True

    def OnSize(self, widget, sizeAlloc):
        cefpython.WindowUtils.OnSize(self.container.get_window().handle, 0, 0, 0)

    def OnClose(self, event):
        print ("Gtk Main Form is closing...")

    def OnExit(self):
        self.exiting = True
        gtk.main_quit()

class GtkLoginForm(Singleton):
    def __init__(self, login_url, redirect_url, cookie_path):
        self.__login_url__ = login_url
        self.__redirect_url__ = redirect_url
        self.__cookie_path__ = cookie_path
        self.__cookie_refresh_time_diff__ = 60 * 60

    def CheckCookie(self):
        self.obj_locker.acquire(1)
        try:
            bRefresh = False
            if os.path.exists(self.__cookie_path__):
                import time
                (mode, ino, dev, nlink, uid, gid, size, atime, mtime, ctime) = os.stat(self.__cookie_path__)
                current_time = time.time()
                time_diff = current_time - mtime
                if time_diff > self.__cookie_refresh_time_diff__:
                    os.remove(self.__cookie_path__)
                    bRefresh = True
            else:
                bRefresh = True
            return bRefresh
        except Exception as e:
            print e
        finally:
            self.obj_locker.release()

    def Show(self):
        print "in GtkLoginForm Show"
        self.obj_locker.acquire(1)
        try:
            print "in GtkLoginForm acquire"
            if not os.path.exists(self.__cookie_path__):
                print "in GtkLoginForm call subprocess"
                current_file_dir = os.path.dirname(__file__)
                current_file_name = current_file_dir + "//WebkitLoginHelper.py"
                import subprocess
                cmd = [
                        'python',
                        current_file_name,
                        self.__login_url__,
                        self.__redirect_url__,
                        self.__cookie_path__
                ]
                p = subprocess.Popen(cmd)
                p.wait()
            else:
                print "in GtkLoginForm cookie already created!"
        except Exception as e:
            print e
        finally:
            self.obj_locker.release()

if __name__ == '__main__':
    # login_url = sys.argv[1]
    # redirect_url = sys.argv[2]
    # cookie_path = sys.argv[3]
    login_url = 'http://www.qixin.com/login'
    redirect_url = 'http://www.qixin.com/'
    cookie_path = 'D://qixin/qixin.cookie'

    sys.excepthook = ExceptHook

    cefpython.Initialize(g_applicationSettings, {})
    gobject.threads_init()
    GtkBrowserForm(login_url,
                   redirect_url,
                   cookie_path)
    gtk.main()
    cefpython.Shutdown()